import axios, {AxiosRequestConfig, Method} from 'axios';

interface IHttpClientRequestParameter<T> {
    method?: Method;
    url: string;
    headers?: object,
    requireToken: boolean;
    // 服务台接口的请求头是特殊的，而且使用不多，所以是可选
    helpDesk?: boolean;
    // token 无视种类，由调用者决定
    token?: string;
    // 查询参数
    params?: T;
    // 请求体参数
    data?: T;
}

export interface IHttpClient {

    doRequest<T>(parameters: IHttpClientRequestParameter<T>): Promise<T>;

    get<T>(parameters: IHttpClientRequestParameter<T>): Promise<T>;

    post<T>(parameters: IHttpClientRequestParameter<T>): Promise<T>;

    delete<T>(parameters: IHttpClientRequestParameter<T>): Promise<T>;
}

function handleToken<T>(parameters: IHttpClientRequestParameter<T>, options: AxiosRequestConfig, reject: (reason?: any) => void) {
    const {requireToken, token} = parameters;
    const helpDesk: boolean = parameters.helpDesk || false;
    if (requireToken) {
        if (token === undefined) {
            reject(new Error('if you set requireToken as true, that you must set token\'s value'));
        }
        if (helpDesk) {
            options.headers['X-Lark-Helpdesk-Authorization'] = `Bearer ${token}`;
        } else {
            options.headers['Authorization'] = `Bearer ${token}`;
        }
    }
}

export class HttpClient implements IHttpClient {
    doRequest<T>(parameters: IHttpClientRequestParameter<T>): Promise<T> {
        const defaultHeaders = {
            'Content-Type': 'application/json; charset=utf-8',
        };
        return new Promise<T>((resolve, reject) => {
            const {params, url, data, method, headers} = parameters;
            const options: AxiosRequestConfig = {
                method,
                url,
                headers: headers === undefined ? defaultHeaders : headers,
                params: params === undefined ? {} : params,
                data: data === undefined ? {} : data,
            };
            handleToken(parameters, options, reject);
            options.maxBodyLength = Infinity;
            axios(options)
                .then((res: any) => {
                    resolve(res as T);
                })
                .catch((err: any) => {
                    reject(err);
                });
        });
    }

    get<T>(parameters: IHttpClientRequestParameter<T>): Promise<T> {
        parameters.method = 'GET';
        return this.doRequest(parameters);
    }

    post<T>(parameters: IHttpClientRequestParameter<T>): Promise<T> {
        parameters.method = 'POST';
        return this.doRequest(parameters);
    }

    delete<T>(parameters: IHttpClientRequestParameter<T>): Promise<T> {
        parameters.method = 'DELETE';
        return this.doRequest(parameters);
    }
}

export const httpClient = new HttpClient();
